/*******************************************************************************
 * Copyright (c) 2001-2003 Boris Pruessmann and others. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Common Public License v0.5 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/cpl-v05.html
 *
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.views;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.api.IPerforceListener;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.resources.RemoteFile;
import net.sourceforge.perforce.core.resources.RemoteFolder;
import net.sourceforge.perforce.core.resources.RemoteResource;
import net.sourceforge.perforce.ui.IPerforceUIConstants;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.action.OpenRemoteFileAction;
import net.sourceforge.perforce.ui.internal.Policy;
import net.sourceforge.perforce.ui.internal.filters.DeletedFilesFilter;
import net.sourceforge.perforce.ui.model.AllRootsElement;
import net.sourceforge.perforce.ui.model.RemoteContentProvider;
import net.sourceforge.perforce.ui.wizards.NewLocationWizard;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;

/**
 * @version $Revision: 494 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class ServerView extends ViewPart implements IPerforceListener {
  //----------------------------------------------------------------------------
  // Public Constants

  /** Unique identifier of the Server view. */
  public static final String ID = PerforceUIPlugin.ID + ".views.ServerView"; //$NON-NLS-1$

  // ---------------------------------------------------------------------------
  // Private Constants

  /** Used for storing state in the memento. */
  private static final String TAG_SHOW_DELETED_FILES = "showDeletedFiles"; //$NON-NLS-1$

  // ---------------------------------------------------------------------------
  // Private Variables

  /** The Root element. */
  private AllRootsElement root;
  /** Stores the tree viewer used to display the perforce servers. */
  private TreeViewer viewer;
  /** Sotres the drill down adapter. */
  private DrillDownAdapter drillPart;
  /** Stores the action for "New->Perforce Server" */
  private Action newAction;
  /** Stores the action for "Refresh". */
  private Action refreshAction;
  /** Stores the action for "Properties". */
  private Action propertiesAction;
  /** Stores the action for "Show Delete Depot Files" */
  private Action showDeletedFilesAction;
  /** Stores the action used on double clicking. */
  private OpenRemoteFileAction openRemoteFileAction;
  /** Stores the memento. */
  private IMemento memento;
  /** Stores the deleted files filter. */
  private DeletedFilesFilter deletedFilesFilter;

  //----------------------------------------------------------------------------
  // -- Public Methods

  @Override
  public void init(IViewSite site, IMemento memento) throws PartInitException {
    super.init(site, memento);
    this.memento = memento;
  }

  @Override
  public void createPartControl(Composite parent) {
    root = new AllRootsElement();

    viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
    viewer.setContentProvider(new RemoteContentProvider());
    viewer.setLabelProvider(createLabelProvider());
    viewer.setSorter(new InternalSorter());
    viewer.getControl().addKeyListener(new KeyAdapter() {
      @Override
      public void keyPressed(KeyEvent event) {
        if (event.keyCode == SWT.F5) {
          getRefreshAction().run();
        } else if (event.keyCode == SWT.INSERT) {
          getNewAction().run();
        }
      }
    });

    getSite().setSelectionProvider(viewer);
    viewer.setInput(root);

    contributeActions();
    memento = null;

    PerforceCorePlugin.getServerManager().addPerforceListener(this);
  }

  @Override
  public void dispose() {
    PerforceCorePlugin.getServerManager().removePerforceListener(this);
    super.dispose();
  }

  @Override
  public void setFocus() {
    viewer.getControl().setFocus();
  }

  @Override
  public void saveState(IMemento memento) {
    if (deletedFilesFilter == null) {
      memento.putString(TAG_SHOW_DELETED_FILES, "true");
    } else {
      memento.putString(TAG_SHOW_DELETED_FILES, "false");
    }
  }

  // ---------------------------------------------------------- Public Methods
  // (IPerforceListener)

  /** @see IPerforceListener#perforceServerAdded(IPerforceServer) */
  public void perforceServerAdded(IPerforceServer server) {
    Display display = getSite().getShell().getDisplay();
    display.asyncExec(new Runnable() {
      public void run() {
        viewer.refresh();
      }
    });
  }

  /** @see IPerforceListener#perforceServerRemoved(IPerforceServer) */
  public void perforceServerRemoved(IPerforceServer server) {
    Display display = getSite().getShell().getDisplay();
    display.asyncExec(new Runnable() {
      public void run() {
        viewer.refresh();
      }
    });
  }

  //----------------------------------------------------------------------------
  // --------- Methods

  /**
   * Returns the TreeViewer.
   *
   * @return the viewer.
   */
  TreeViewer getViewer() {
    return viewer;
  }

  /**
   * Call this method to retrieve the drill part.
   *
   * @return the drill part.
   */
  DrillDownAdapter getDrillPart() {
    return drillPart;
  }

  /**
   * Call this method to retrieve the new action.
   *
   * @return the action.
   */
  Action getNewAction() {
    return newAction;
  }

  /**
   * Call this method to retrieve the refresh action.
   *
   * @return the refresh action.
   */
  Action getRefreshAction() {
    return refreshAction;
  }

  /**
   * Handles double clicks.
   *
   * @param e the DoubleClick event.
   */
  void handleDoubleClick(DoubleClickEvent e) {
    ISelection selection = e.getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection structured = (IStructuredSelection) selection;
      if (structured.size() == 1) {
        Object first = structured.getFirstElement();
        if (first instanceof RemoteFile) {
          // It's a file, open it.
          openRemoteFileAction.selectionChanged(null, selection);
          openRemoteFileAction.run(null);
        } else {
          // Try to expand/contract
          viewer.setExpandedState(first, !viewer.getExpandedState(first));
        }
      }
    }
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  /**
   * Called to create the label provider.
   *
   * @return IBaseLabelProvider
   */
  private IBaseLabelProvider createLabelProvider() {
    return new ServerViewLabelProvider();
  }

  /** Creates and registers actions with menu and toolbar. */
  private void contributeActions() {
    createRefreshAction();
    createNewAction();
    createPropertiesAction();
    createShowDeletedFilesAction();
    createOpenRemoteFileAction();

    drillPart = new DrillDownAdapter(viewer);
    contributeMenuActions();
    contributeToolbarActions();
    contributeDoubleClickListener();
  }

  /** Called to create the action used to open remote files. */
  private void createOpenRemoteFileAction() {
    openRemoteFileAction = new OpenRemoteFileAction();
  }

  /** This method add double click listeners to the view. */
  private void contributeDoubleClickListener() {
    viewer.addDoubleClickListener(new IDoubleClickListener() {
      public void doubleClick(DoubleClickEvent e) {
        handleDoubleClick(e);
      }
    });
  }

  /** This method contributes the created actions to the toolbar. */
  private void contributeToolbarActions() {
    // Create the local tool bar
    IActionBars bars = getViewSite().getActionBars();
    IToolBarManager toolBarManager = bars.getToolBarManager();
    drillPart.addNavigationActions(toolBarManager);
    toolBarManager.add(refreshAction);
    toolBarManager.update(false);

    bars.getMenuManager().add(showDeletedFilesAction);
  }

  /** This method contributes the created action to the context menu. */
  private void contributeMenuActions() {
    Tree tree = viewer.getTree();

    MenuManager menuManager = new MenuManager();
    Menu menu = menuManager.createContextMenu(tree);
    menuManager.addMenuListener(new IMenuListener() {
      /** @see IMenuListener#menuAboutToShow(IMenuManager) */
      public void menuAboutToShow(IMenuManager manager) {
        MenuManager newMenuManager = new MenuManager(Policy.bind("ServerView.newSubMenu"), //$NON-NLS-1$
            IWorkbenchActionConstants.GROUP_ADD);
        newMenuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
        newMenuManager.add(getNewAction());
        manager.add(newMenuManager);

        MenuManager goToMenuManager = new MenuManager(Policy.bind("ServerView.goToSubMenu"), //$NON-NLS-1$
            IWorkbenchActionConstants.GROUP_ADD);
        getDrillPart().addNavigationActions(goToMenuManager);
        manager.add(goToMenuManager);

        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
        manager.add(new Separator());
        manager.add(getRefreshAction());
        manager.add(propertiesAction);
      }
    });
    menuManager.setRemoveAllWhenShown(true);
    tree.setMenu(menu);
    getSite().registerContextMenu(menuManager, viewer);
  }

  /** Called to create the "New->Perforce Server" action. */
  private void createNewAction() {
    ImageDescriptor descriptor =
        PerforceUIPlugin.getPlugin().getImageDescriptor(IPerforceUIConstants.IMG_NEWLOCATION);

    newAction = new Action(Policy.bind("ServerView.new"), descriptor) //$NON-NLS-1$
        {
          @Override
          public void run() {
            NewLocationWizard wizard = new NewLocationWizard();
            WizardDialog dialog = new WizardDialog(getViewer().getTree().getShell(), wizard);
            dialog.open();
          }
        };
  }

  /** Called to create the "Properties" action. */
  private void createPropertiesAction() {
    propertiesAction = new PropertyDialogAction(viewer.getTree().getShell(), viewer);
  }


  /** Called to create the "Refresh" action. */
  private void createRefreshAction() {
    ImageDescriptor descriptor =
        PerforceUIPlugin.getPlugin().getImageDescriptor(IPerforceUIConstants.IMG_REFRESH);

    refreshAction = new Action(Policy.bind("ServerView.refresh"), descriptor) //$NON-NLS-1$
        {
          @Override
          public void run() {
            getViewer().refresh();
          }
        };
  }

  /** Called to create the "Show Deleted Files" action. */
  private void createShowDeletedFilesAction() {
    boolean state = false;
    if (memento != null) {
      String stateString = memento.getString(TAG_SHOW_DELETED_FILES);
      state = Boolean.valueOf(stateString).booleanValue();
      if (!state) {
        deletedFilesFilter = new DeletedFilesFilter();
        viewer.addFilter(deletedFilesFilter);
      }
    }

    showDeletedFilesAction = new Action(Policy.bind("ServerView.showDeletedFiles.label")) //$NON-NLS-1$
        {
          @Override
          public void run() {
            if (isChecked()) {
              getViewer().removeFilter(deletedFilesFilter);
              deletedFilesFilter = null;
            } else {
              deletedFilesFilter = new DeletedFilesFilter();
              getViewer().addFilter(deletedFilesFilter);
            }
          }
        };

    showDeletedFilesAction.setToolTipText(Policy.bind("ServerView.showDeletedFiles.tooltip")); //$NON-NLS-1$

    showDeletedFilesAction.setChecked(state);
  }

  // ----------------------------------------------------------------------
  // Private Nested Classes

  /**
   * Class responsible for sorting the tree.
   */
  private static class InternalSorter extends ViewerSorter {
    private static final String nameOf(Object o) {
      if (o instanceof IPerforceServer) {
        return ((IPerforceServer) o).getUniqueName();
      }
      if (o instanceof RemoteResource) {
        return ((RemoteResource) o).getName();
      }
      return o.toString();
    }

    @Override
    public int compare(Viewer viewer, Object e1, Object e2) {
      return nameOf(e1).compareTo(nameOf(e2));
    }
  }


  /** Label/Color Provider for Servers Tree */
  private static class ServerViewLabelProvider extends WorkbenchLabelProvider implements
      IColorProvider {
    // -----------------------------------------------------------------------
    // Private Variables

    /** Stores the border color */
    private Color foregroundColor;

    // ---------------------------------------------------------------------
    // Public Constructors

    /** Constructor for ServerViewLabelProvider. */
    public ServerViewLabelProvider() {
      Display display = Display.getCurrent();
      foregroundColor = new Color(display, new RGB(0, 0, 255));
    }

    //--------------------------------------------------------------------------
    // Public Methods

    @Override
    public Color getForeground(Object element) {
      if (!(element instanceof RemoteFolder)) {
        return null;
      }

      RemoteFolder folder = (RemoteFolder) element;

      IWorkspaceRoot root = PerforceUIPlugin.getWorkspace().getRoot();
      IPath folderPath = folder.getLocation();
      if (folderPath == null) {
        return null;
      }
      IContainer container = root.getContainerForLocation(folderPath);
      if (container == null) {
        return null;
      } else {
        return foregroundColor;
      }
    }

    @Override
    public Color getBackground(Object element) {
      return null;
    }
  }
}
